package tech.xs.auth.service.impl;

import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import tech.xs.auth.dao.AuthSysUserDao;
import tech.xs.auth.domain.entity.AuthSysUser;
import tech.xs.auth.domain.exception.AuthException;
import tech.xs.auth.service.AuthTokenService;
import tech.xs.common.auth.domain.cache.AuthTokenCache;
import tech.xs.common.auth.domain.enums.AuthClientTypeEnum;
import tech.xs.common.framework.domain.model.ApiResult;
import tech.xs.common.auth.util.AuthTokenUtil;
import tech.xs.sys.api.SysUserRoleApi;
import tech.xs.sys.api.domain.SysRoleBo;

import javax.annotation.Resource;
import java.time.Duration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

@Slf4j
@Service
public class AuthTokenServiceImpl implements AuthTokenService {


    @Resource
    private RedissonClient redissonClient;

    @Resource
    private SysUserRoleApi sysUserRoleApi;

    @Resource
    private AuthSysUserDao loginUserDao;

    @Override
    public AuthTokenCache createAuthToken(AuthSysUser user, AuthClientTypeEnum clientType) {
        AuthTokenCache authToken = new AuthTokenCache();
        authToken.setUserId(user.getId());
        authToken.setUserName(user.getUserName());
        authToken.setClientType(clientType);
        authToken.setAccessToken(AuthTokenUtil.createTokenStr(user.getId(), clientType));
        authToken.setRefreshToken(AuthTokenUtil.createTokenStr(user.getId(), clientType));
        // 设置角色code列表
        authToken.setRoleCodes(getUserRoleCodes(user));

        switch (clientType) {
            case UNKNOWN:
            case BROWSER:
            case APP: {
                authToken.setAccessTokenInvalidTime(System.currentTimeMillis() + clientType.getAccessTokenValidity());
                authToken.setRefreshTokenInvalidTime(System.currentTimeMillis() + clientType.getRefreshTokenValidity());
                break;
            }
            case SERVER:
            case TOOL: {
                authToken.setAccessTokenInvalidTime(Long.MAX_VALUE);
                authToken.setRefreshTokenInvalidTime(Long.MAX_VALUE);
                break;
            }
            default: {
                authToken.setAccessTokenInvalidTime(System.currentTimeMillis() + AuthClientTypeEnum.UNKNOWN.getAccessTokenValidity());
                authToken.setRefreshTokenInvalidTime(System.currentTimeMillis() + AuthClientTypeEnum.UNKNOWN.getRefreshTokenValidity());
            }
        }

        log.info("创建token" + authToken);
        RBucket<AuthTokenCache> bucket = redissonClient.getBucket(AuthTokenUtil.getTokenCacheKey(user.getId(), clientType));
        bucket.set(authToken, Duration.ofMillis(clientType.getAccessTokenValidity()));
        return authToken;
    }

    private Set<String> getUserRoleCodes(AuthSysUser user) {
        ApiResult<List<SysRoleBo>> rolesResult = sysUserRoleApi.getRolesByUserName(user.getUserName());
        if (!rolesResult.isSuccess()) {
            throw new AuthException("获取用户角色异常 用户:" + user);
        }
        Set<String> roleCodes = new HashSet<>();
        List<SysRoleBo> roles = rolesResult.getData();
        if (roles != null) {
            for (SysRoleBo role : roles) {
                roleCodes.add(role.getCode());
            }
        }
        return roleCodes;
    }

    @Override
    public void removeAuthToken(Long userId, AuthClientTypeEnum clientType) {
        redissonClient.getBucket(AuthTokenUtil.getTokenCacheKey(userId, clientType)).delete();
    }

    @Override
    public void updateToken(Long userId) {
        AuthClientTypeEnum[] clientTypes = AuthClientTypeEnum.values();
        for (AuthClientTypeEnum clientType : clientTypes) {
            RBucket<AuthTokenCache> bucket = redissonClient.getBucket(AuthTokenUtil.getTokenCacheKey(userId, clientType));
            if (!bucket.isExists()) {
                continue;
            }
            AuthSysUser user = loginUserDao.selectById(userId);
            if (user == null) {
                log.warn("更新用户Token 用户不存在");
            }
            AuthTokenCache token = bucket.get();
            token.setUserName(user.getUserName());
            // 设置角色code列表
            token.setRoleCodes(getUserRoleCodes(user));
            bucket.set(token);
        }
    }


}
